package cn.devcat.sso.service.impl;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import javax.annotation.Resource;
import javax.mail.MessagingException;
import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.security.crypto.factory.PasswordEncoderFactories;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageInfo;

import cn.devcat.sso.config.exception.DevcatUserException;
import cn.devcat.sso.config.exception.SendMailException;
import cn.devcat.sso.config.exception.WarningException;
import cn.devcat.sso.config.mail.SendMailFactory;
import cn.devcat.sso.config.security.handler.SecurityLimitUtil;
import cn.devcat.sso.entity.DevcatRole;
import cn.devcat.sso.entity.DevcatUser;
import cn.devcat.sso.entity.DevcatUserInfo;
import cn.devcat.sso.entity.PageData;
import cn.devcat.sso.entity.SearchCommonParam;
import cn.devcat.sso.entity.vo.NormalSetUser;
import cn.devcat.sso.entity.vo.UserVO;
import cn.devcat.sso.enums.EmailTypeEnum;
import cn.devcat.sso.enums.OptionTypeEnum;
import cn.devcat.sso.mapper.DevcatUserMapper;
import cn.devcat.sso.service.IDevcatRoleService;
import cn.devcat.sso.service.IDevcatUserInfoService;
import cn.devcat.sso.service.IDevcatUserService;
import cn.devcat.sso.service.ISendMailService;
import cn.devcat.sso.util.PageHelperUtil;
import cn.devcat.sso.util.RedisUtil;
import cn.devcat.sso.util.UserUtil;

@Service
public class DevcatUserServiceImpl extends ServiceImpl<DevcatUserMapper, DevcatUser> implements IDevcatUserService {

	@Resource
	DevcatUserMapper userMapper;

	@Autowired
	IDevcatUserInfoService userInfoService;

	@Autowired
	IDevcatRoleService roleService;

	@Autowired
	UserUtil userUtil;

	@Autowired
	SendMailFactory sendMailFactory;

	@Autowired
	RedisUtil redisUtil;

	/**
	 * 根据用户名查询用户
	 */
	@Override
	public DevcatUser getUserByUsername(String username) {
		return getOne(new QueryWrapper<DevcatUser>().eq("username", username));
	}

	@CacheEvict(cacheNames = "roleListByUserId", allEntries = true)
	@Transactional
	@Override
	public void registerUser(DevcatUser user) throws DevcatUserException {
		HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
		SecurityLimitUtil.checkRequestCount(request);
		initUserByRegister(user);
		List<String> roleIds = new LinkedList<>();
		if (user.getRoleList() == null || user.getRoleList().isEmpty()) {
			String userRoleId = roleService.getOne(new QueryWrapper<DevcatRole>().eq("role_code", "ROLE_USER")).getId();
			roleIds.add(userRoleId);
		} else {
			roleIds = user.getRoleList().stream().map(DevcatRole::getId).collect(Collectors.toList());
		}

		save(user);
		userMapper.bindRoleListForUser(user.getId(), roleIds);
		userInfoService.initUserByRegister(user);
	}

	@Override
	public UserVO getUserVOByRequest() {
		DevcatUser userByRequest = userUtil.getUserByRequest();
		UserVO vo = new UserVO();
		vo.setUsername(userByRequest.getUsername());
		vo.setId(userByRequest.getId());
		return vo;
	}

	/**
	 * 根据用户ID获取用户信息(注意要设置管理员权限)
	 */
	@Override
	public DevcatUserInfo getUserInfoByUserId(String userId) {
		return userInfoService.getById(userId);
	}

	/**
	 * 通过token获取用户信息
	 */
	@Override
	public DevcatUserInfo getUserInfoByRequest() {
		DevcatUser userByRequest = userUtil.getUserByRequest();
		DevcatUserInfo userInfo = userInfoService.getById(userByRequest.getId());
		userInfo.setEmail(userByRequest.getEmail());
		userInfo.setEmailStatus(userByRequest.getEmailStatus());
		return userInfo;
	}

	/**
	 * 初始化注册用户
	 *
	 * @param user 注册用户
	 */
	private DevcatUser initUserByRegister(DevcatUser user) throws DevcatUserException {
		checkRegisterUser(user, true, true);
		user.setAccountNonExpired(true);
		user.setAccountNonLocked(true);
		user.setCredentialsNonExpired(true);
		user.setEnabled(true);
		user.setCreateTime(LocalDateTime.now());
		user.setEmailStatus(false);
		String secretPassword = PasswordEncoderFactories.createDelegatingPasswordEncoder().encode(user.getPassword());
		user.setPassword(secretPassword);
		return user;
	}

	@CacheEvict(cacheNames = "roleListByUserId", allEntries = true)
	@Transactional
	@Override
	public void optionUser(String optionType, Boolean optionValue, List<String> ids) {
		OptionTypeEnum typeEnum = OptionTypeEnum.getOptionTypeEnum(optionType);
		switch (typeEnum) {
		case ACCOUNT_EXPIRE: // 账号过期
			update(new UpdateWrapper<DevcatUser>().in("id", ids).set("account_non_expired", optionValue));
			break;
		case ACCOUNT_LOCK: // 账号锁定
			update(new UpdateWrapper<DevcatUser>().in("id", ids).set("account_non_locked", optionValue));
			break;
		case PASSWORD_EXPIRE: // 密码过期
			update(new UpdateWrapper<DevcatUser>().in("id", ids).set("credentials_non_expired", optionValue));
			break;
		case ENABLE: // 启用账号
			update(new UpdateWrapper<DevcatUser>().in("id", ids).set("enabled", optionValue));
			break;
    case ENABLE_EMAIL:
      update(new UpdateWrapper<DevcatUser>().in("id",ids).set("email_status",optionValue));
      break;
		case REMOVE: // 删除用户
			userMapper.clearRoleUserRelationByUserId(ids);
			userInfoService.removeByIds(ids);
			removeBatchByIds(ids);
			break;
		default:
			break;
		}
	}

	@Override
	public void removeUser() {
		DevcatUser userByRequest = userUtil.getUserByRequest();
		optionUser("remove", null, Arrays.asList(userByRequest.getId()));
	}

	/**
	 * 普通用户设置
	 *
	 * @throws DevcatUserException
	 */
	@Override
	public void setUser(NormalSetUser user) throws DevcatUserException {
		DevcatUser userByRequest = userUtil.getUserByRequest();
		if (StringUtils.isNotEmpty(user.getUsername())) {
			setUsername(userByRequest, user.getUsername());
		} else if (StringUtils.isNotEmpty(user.getOldPassword()) && StringUtils.isNotEmpty(user.getNewPassword())) {
			setPassword(userByRequest, user.getOldPassword(), user.getNewPassword());
		} else if (StringUtils.isNotEmpty(user.getEmail())) {
			setEmail(userByRequest, user.getEmail());
		} else {
			throw new DevcatUserException("输入的参数不能为空");
		}
	}

	/**
	 * 激活邮箱
	 *
	 * @throws SendMailException
	 * @throws MessagingException
	 */
	@Override
	public void enableEmail(String enableCode) {
		DevcatUser userByRequest = userUtil.getUserByRequest();
		if (userByRequest.getEmailStatus()) {
			throw new SendMailException("邮箱已经是激活状态，无需重新激活");
		}
		String username = userByRequest.getUsername();
		boolean flag = redisUtil.checkKey("enable:" + username);
		if (StringUtils.isEmpty(enableCode)) {
			if (flag) {
				throw new WarningException("已发送的验证码还未过期，请到对应邮箱检查后重新填入激活");
			}
			ISendMailService createSendMailService = sendMailFactory
					.createSendMailService(EmailTypeEnum.REGISTER_MAILL);
			try {
				createSendMailService.sendMail(userByRequest);
			} catch (MessagingException e) {
				// TODO Auto-generated catch block
				throw new SendMailException(e.getMessage());
			}
		} else {
			if (redisUtil.checkKey("enable:" + username)) {
				String rightEnableCode = redisUtil.select("enable:" + username);
				if (rightEnableCode.equalsIgnoreCase(enableCode)) {
					// 对比成功后激活邮箱
					update(new UpdateWrapper<DevcatUser>().eq("username", username).set("email_status", true));
				} else {
					throw new RuntimeException("激活码错误，请输入正确的激活码");
				}
			} else {
				throw new RuntimeException("激活码已过期，请重新发送激活邮件");
			}
		}

	}

	/**
	 * 找回密码
	 */
	@Override
	public void findPassword(String username, String code, String resetPassword) {
		DevcatUser user = getUserByUsername(username);
		String key = "find:" + username;
		if (StringUtils.isEmpty(code)) {
			if (redisUtil.checkKey(key)) {
				throw new WarningException("验证码已发送并且未过期，请前往邮箱查看，请勿重复发送");
			}
			ISendMailService createSendMailService = sendMailFactory
					.createSendMailService(EmailTypeEnum.FORGETPASSWORD_MAIL);
			try {
				System.out.println(JSONObject.toJSONString(user));
				createSendMailService.sendMail(user);
			} catch (MessagingException e) {
				throw new SendMailException(e.getMessage());
			}
		} else {
			if (!redisUtil.checkKey(key)) {
				throw new DevcatUserException("验证码已过期，请重新发送找回邮件");
			}else {
				if(!redisUtil.select(key).equalsIgnoreCase(code)) {
					throw new SendMailException("验证码错误，请确认后重试");
				}else {
					if(StringUtils.isNotEmpty(resetPassword)){
						String password = PasswordEncoderFactories.createDelegatingPasswordEncoder().encode(resetPassword);
						update(new UpdateWrapper<DevcatUser>().eq("username", username).set("password", password));
					}
				}
			}
		}

	}

	/**
	 * 普通用户更新用户名
	 *
	 * @throws DevcatUserException
	 */
	private void setUsername(DevcatUser userByRequest, String username) throws DevcatUserException {
		String usernameReg = "^[a-zA-Z][a-zA-Z0-9_]{4,15}$";
		boolean usernameCheckResult = username.matches(usernameReg);
		if (!usernameCheckResult) {
			throw new DevcatUserException("用户名应该由字母开头可以包含数字或者下划线的5到16位字符组成");
		}
		long count = count(new QueryWrapper<DevcatUser>().eq("username", username));
		if (count > 0) {
			throw new DevcatUserException("用户名已存在，请更换后重试");
		}
		update(new UpdateWrapper<DevcatUser>().eq("id", userByRequest.getId()).set("username", username));
		userInfoService
				.update(new UpdateWrapper<DevcatUserInfo>().eq("id", userByRequest.getId()).set("username", username));
	}

	/**
	 * 更新密码
	 *
	 * @throws DevcatUserException
	 */
	private void setPassword(DevcatUser userByRequest, String oldPassword, String newPassword)
			throws DevcatUserException {
		String passwordReg = "^[a-zA-Z]\\w{5,17}$"; // 6~18位数字、字母或下划线
		boolean passwordCheckResult = newPassword.matches(passwordReg);
		if (!passwordCheckResult) {
			throw new DevcatUserException("密码应该由字母、数字或者下划线组成的的6到18位字");
		}
		String currentPassword = userByRequest.getPassword();
		boolean matches = PasswordEncoderFactories.createDelegatingPasswordEncoder().matches(oldPassword,
				currentPassword);
		if (!matches) {
			throw new DevcatUserException("旧密码校验失败，请重新设置");
		}
		String updatePassword = PasswordEncoderFactories.createDelegatingPasswordEncoder().encode(newPassword);
		update(new UpdateWrapper<DevcatUser>().eq("id", userByRequest.getId()).set("password", updatePassword));
	}

	/**
	 * 设置邮箱
	 *
	 * @param email
	 * @throws DevcatUserException
	 */
	private void setEmail(DevcatUser userByRequest, String email) throws DevcatUserException {
		String emailReg = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$";
		boolean emailCheckResult = email.matches(emailReg);
		if (!emailCheckResult) {
			throw new DevcatUserException("注册邮箱格式错误,请填写正确的邮箱");
		}
		long count = count(new QueryWrapper<DevcatUser>().eq("email", email).eq("email_status", true));
		if (count > 0) {
			throw new DevcatUserException("改邮箱已被注册并激活，修改失败");
		}
//		Boolean emailStatus = userByRequest.getEmailStatus();
//		if(emailStatus) {
//			throw new DevcatUserException("您的账号以激活了绑定的邮箱，如果要修改请先解绑邮箱");
//		}
		update(new UpdateWrapper<DevcatUser>().eq("id", userByRequest.getId()).set("email", email).set("email_status",
				false));
	}

	/**
	 * 更新用户
	 *
	 * @throws DevcatUserException
	 */
	@Override
	public void updateDevcatUser(DevcatUser devcatUser) throws DevcatUserException {
		// TODO Auto-generated method stub

		String temp = devcatUser.getPassword().split("\\$")[0];
		if (!"{bcrypt}".equals(temp)) { // 如果密码重置则需要校验密码合法性
			checkRegisterUser(devcatUser, false, true);
			String secretPassword = PasswordEncoderFactories.createDelegatingPasswordEncoder()
					.encode(devcatUser.getPassword());
			devcatUser.setPassword(secretPassword);
		} else {
			checkRegisterUser(devcatUser, false, false); // 密码没有重置，直接
		}
		updateById(devcatUser);
	}

	/**
	 * 查询用户
	 */
	@Override
	public PageData<DevcatUser> getUserList(SearchCommonParam param) {
		QueryWrapper<DevcatUser> wrapper = new QueryWrapper<>();
		if (StringUtils.isNotEmpty(param.getKeyword())) {
			wrapper.like("username", param.getKeyword()).or().like("email", param.getKeyword());
		}
		wrapper.orderByDesc("create_time");
		PageHelperUtil.handle(param);
		List<DevcatUser> list = list(wrapper);
		list.forEach(item -> item.setRoleList(roleService.getRoleListByUserId(item.getId())));
		return PageHelperUtil.makePageData(new PageInfo<DevcatUser>(list));
	}

	@CacheEvict(cacheNames = "roleListByUserId", allEntries = true)
	@Override
	public void bindRoleListForUserBatch(List<String> userIds, List<String> roleIds) {
		userMapper.clearRoleUserRelationByUserId(userIds);
		for (String userId : userIds) {
			userMapper.bindRoleListForUser(userId, roleIds);
		}
	}

	/**
	 * 校验注册用户参数
	 *
	 * @param user
	 * @param isSlef 校验时是否包含自己
	 */
	private void checkRegisterUser(DevcatUser user, Boolean isSlef, Boolean needCheckPassword)
			throws DevcatUserException {
		// 用户名必须是5~16位字母、数字和下划线，可以任意组成
		String usernameReg = "^[a-zA-Z][a-zA-Z0-9_]{4,15}$";
		boolean usernameCheckResult = user.getUsername().matches(usernameReg);
		if (!usernameCheckResult) {
			throw new DevcatUserException("用户名应该由字母开头可以包含数字或者下划线的5到16位字符组成");
		}
		if (needCheckPassword) {
			String passwordReg = "^[a-zA-Z]\\w{5,17}$"; // 6~18位数字、字母或下划线
			boolean passwordCheckResult = user.getPassword().matches(passwordReg);
			if (!passwordCheckResult) {
				throw new DevcatUserException("密码应该由字母、数字或者下划线组成的的6到18位字");
			}
		}
		String emailReg = "^\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*$";
		boolean emailCheckResult = user.getEmail().matches(emailReg);
		if (!emailCheckResult) {
			throw new DevcatUserException("注册邮箱格式错误,请填写正确的邮箱");
		}
		// 校验用户名是否已经存在
		QueryWrapper<DevcatUser> wrapperForUsername = new QueryWrapper<DevcatUser>().eq("username", user.getUsername());
		if (!isSlef) {
			wrapperForUsername.ne("username", user.getUsername());
		}
		long count = count(wrapperForUsername);
		if (count > 0) {
			throw new DevcatUserException("用户名重复，注册失败");
		}
		// 校验邮箱地址是否已经存在，如果存在会校验是否已经激活，如果没有激活也会认为不存在
		QueryWrapper<DevcatUser> wrapperForEmail = new QueryWrapper<DevcatUser>().eq("email", user.getUsername())
				.eq("email_status", true);
		if (!isSlef) {
			wrapperForEmail.ne("email", user.getEmail()).eq("email_status", true);
		}
		long count2 = count(wrapperForEmail);
		if (count2 > 0) {
			throw new DevcatUserException("邮箱地址已存在，请更换邮箱地址，或登录原有账号解绑邮箱后重试");
		}
	}

}
